home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / Scrawl / scrawl.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  32.1 KB  |  1,012 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Scrawl.cpp
  3. //
  4. // Desc: Demonstrates an application which receives relative mouse data
  5. //       in non-exclusive mode via a dialog timer.
  6. //
  7. // Copyright (c) Microsoft Corporation. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #define DIRECTINPUT_VERSION 0x0800
  11.  
  12. #include <tchar.h>
  13. #include <windows.h>
  14. #include <commctrl.h>
  15. #include <windowsx.h>
  16. #include <basetsd.h>
  17. #include <dinput.h>
  18. #include "resource.h"
  19.  
  20.  
  21.  
  22.  
  23. //-----------------------------------------------------------------------------
  24. // Function prototypes 
  25. //-----------------------------------------------------------------------------
  26. HRESULT InitVariables();
  27. HWND    RegisterWindowClass( HINSTANCE hInst );
  28. LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
  29.  
  30. HRESULT OnClear( HWND hWnd );
  31. VOID    OnAbout( HWND hWnd );
  32. VOID    InvalidateCursorRect(HWND hWnd);
  33. VOID    OnPaint( HWND hWnd );
  34. BOOL    OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct );
  35. VOID    OnInitMenuPopup( HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu );
  36. VOID    OnKeyDown( HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags );
  37. HRESULT InitDirectInput( HWND hWnd );
  38. HRESULT SetAcquire();
  39. HRESULT FreeDirectInput();
  40. VOID    OnMouseInput( HWND hWnd );
  41. VOID    OnLeftButtonDown( HWND hWnd );
  42. VOID    OnRightButtonUp( HWND hWnd );
  43.  
  44.  
  45.  
  46.  
  47. //-----------------------------------------------------------------------------
  48. // Defines, constants, and global variables
  49. //-----------------------------------------------------------------------------
  50. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  51. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  52.  
  53. #define SCRAWL_CXBITMAP             512
  54. #define SCRAWL_CYBITMAP             300
  55. #define SAMPLE_BUFFER_SIZE           16
  56. #define IDC_CLEAR               64
  57. #define IDC_ABOUT               65
  58.  
  59. struct LEFTBUTTONINFO 
  60. {
  61.     HDC   hdcWindow;
  62.     BOOL  bMoved;
  63.     DWORD dwSeqLastSeen;
  64. };
  65.  
  66.  
  67. HDC     g_hDC           = NULL; // Memory DC our picture lives in 
  68. HBITMAP g_hBitmap       = NULL; // Our picture 
  69. HBITMAP g_hbmpDeselect  = NULL; // Stock bitmap for deselecting 
  70. HCURSOR g_hCursorCross  = NULL; // cross hair
  71. HMENU   g_hMenuSense    = NULL; // Sensitivity popup menu
  72. int     g_cxCross;              // Width of crosshairs cursor 
  73. int     g_cyCross;              // Height of crosshairs cursor 
  74. int     g_dxCrossHot;           // Hotspot location of crosshairs 
  75. int     g_dyCrossHot;           // Hotspot location of crosshairs 
  76. BOOL    g_bShowCursor   = TRUE; // Should the cursor be shown? 
  77. int     g_x;                    // Virtual x-coordinate 
  78. int     g_y;                    // Virtual y-coordinate 
  79. int     g_dxFuzz;               // Leftover x-fuzz from scaling 
  80. int     g_dyFuzz;               // Leftover y-fuzz from scaling 
  81. int     g_iSensitivity;         // Mouse sensitivity 
  82.  
  83. LPDIRECTINPUT8          g_pDI           = NULL;         
  84. LPDIRECTINPUTDEVICE8    g_pMouse        = NULL;     
  85. HANDLE                  g_hMouseEvent   = NULL;
  86. BOOL                    g_bActive       = TRUE;
  87. BOOL                    g_bSwapMouseButtons;
  88.  
  89.  
  90.  
  91.  
  92. //-----------------------------------------------------------------------------
  93. // Name: WinMain()
  94. // Desc: Entry point for the application.  Since we use a simple dialog for 
  95. //       user interaction we don't need to pump messages.
  96. //-----------------------------------------------------------------------------
  97. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
  98. {
  99.     HRESULT hr;
  100.     HWND    hWnd;
  101.     BOOL    bDone;
  102.     DWORD   dwResult;
  103.     MSG     msg;
  104.  
  105.     InitCommonControls();
  106.  
  107.     // Initialize global varibles
  108.     if ( FAILED( hr = InitVariables() ) )
  109.     {
  110.         MessageBox( NULL, _T("Error Initializing Variables"), 
  111.                     _T("Scrawl"),  MB_ICONERROR | MB_OK );
  112.         return TRUE;
  113.     }
  114.  
  115.     // Display the main dialog box.
  116.     hWnd = RegisterWindowClass( hInstance );
  117.     if( NULL == hWnd )
  118.     {
  119.         MessageBox( NULL, _T("Error Creating Window"), 
  120.                     _T("Scrawl"), MB_ICONERROR | MB_OK );
  121.         return TRUE;
  122.     }
  123.  
  124.     // Start message pump. Since we use notification handles, we need to use
  125.     // MsgWaitForMultipleObjects() to wait for the event or a message, 
  126.     // whichever comes first.
  127.  
  128.     bDone = FALSE;
  129.     while( !bDone ) 
  130.     {
  131.         dwResult = MsgWaitForMultipleObjects( 1, &g_hMouseEvent, 
  132.                                               FALSE, INFINITE, QS_ALLINPUT );
  133.         switch( dwResult ) 
  134.         {
  135.             // WAIT_OBJECT_0 + 0 means that g_hevtMouse was signalled 
  136.             case WAIT_OBJECT_0 + 0:
  137.                 OnMouseInput( hWnd );
  138.                 break;
  139.  
  140.             // WAIT_OBJECT_0 + 1 means that we have messages to process 
  141.             case WAIT_OBJECT_0 + 1:
  142.                 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  143.                 {
  144.                     if( msg.message == WM_QUIT ) 
  145.                     {
  146.                         // Stop loop if it's a quit message
  147.                         bDone = TRUE;
  148.                     } 
  149.                     else 
  150.                     {
  151.                         TranslateMessage( &msg );
  152.                         DispatchMessage( &msg );
  153.                     }
  154.                 }
  155.                 break;
  156.         }
  157.     }
  158.  
  159.     // Clean up DirectInput
  160.     FreeDirectInput();
  161.  
  162.     
  163.     // Release events
  164.     if( g_hMouseEvent )
  165.         CloseHandle( g_hMouseEvent );
  166.  
  167.     // Delete bitmaps
  168.     if( g_hDC ) 
  169.     {
  170.         if( g_hbmpDeselect ) 
  171.             SelectObject( g_hDC, g_hbmpDeselect );
  172.  
  173.         DeleteDC( g_hDC );
  174.     }
  175.  
  176.     if( g_hBitmap ) 
  177.         DeleteObject( g_hBitmap );
  178.  
  179.     // Destroy loaded menus
  180.     if( g_hMenuSense )
  181.         DestroyMenu( g_hMenuSense );
  182.  
  183.     return TRUE;
  184. }
  185.  
  186.  
  187.  
  188.  
  189. //-----------------------------------------------------------------------------
  190. // Name: InitVariables()
  191. // Desc: Initialize global varibles 
  192. //-----------------------------------------------------------------------------
  193. HRESULT InitVariables()
  194. {
  195.     ICONINFO iconInfo;
  196.     BITMAP   bitmap;
  197.  
  198.     // Get our crosshairs cursor and extract the the width and
  199.     // hotspot location so we can draw it manually.
  200.     g_hCursorCross = LoadCursor( NULL, IDC_CROSS );
  201.  
  202.     GetIconInfo( g_hCursorCross, &iconInfo );
  203.     GetObject( iconInfo.hbmMask, sizeof(BITMAP), &bitmap );
  204.  
  205.     // Delete un-needed handles
  206.     if( iconInfo.hbmMask)  
  207.         DeleteObject( iconInfo.hbmMask );
  208.     if( iconInfo.hbmColor) 
  209.         DeleteObject( iconInfo.hbmColor );
  210.  
  211.     // Save x-y info 
  212.     g_dxCrossHot = iconInfo.xHotspot;
  213.     g_dyCrossHot = iconInfo.yHotspot;
  214.  
  215.     g_cxCross = bitmap.bmWidth;
  216.     g_cyCross = bitmap.bmHeight;
  217.  
  218.     // create and setup our scrawl bitmap.
  219.     g_hDC = CreateCompatibleDC( NULL );  
  220.     if( NULL == g_hDC ) 
  221.         return E_FAIL;
  222.  
  223.     g_hBitmap = CreateBitmap( SCRAWL_CXBITMAP, SCRAWL_CYBITMAP, 1, 1, 0 );
  224.     if( NULL == g_hBitmap ) 
  225.         return E_FAIL;
  226.  
  227.     g_hbmpDeselect = (HBITMAP)SelectObject( g_hDC, g_hBitmap );
  228.  
  229.     // Clear bitmap
  230.     OnClear( NULL );  
  231.  
  232.     return S_OK;
  233. }
  234.  
  235.  
  236.  
  237.  
  238. //-----------------------------------------------------------------------------
  239. // Name: RegisterWindowClass()
  240. // Desc: Set up the window class. 
  241. //-----------------------------------------------------------------------------
  242. HWND RegisterWindowClass( HINSTANCE hInst )
  243. {
  244.     WNDCLASS wc;
  245.     wc.hCursor        = LoadCursor( 0, IDC_ARROW );
  246.     wc.hIcon          = LoadIcon( hInst, MAKEINTRESOURCE(IDI_MAIN) );
  247.     wc.lpszMenuName   = NULL;
  248.     wc.lpszClassName  = _T("Scrawl");
  249.     wc.hbrBackground  = NULL;
  250.     wc.hInstance      = hInst;
  251.     wc.style          = 0;
  252.     wc.lpfnWndProc    = WndProc;
  253.     wc.cbClsExtra     = 0;
  254.     wc.cbWndExtra     = 0;
  255.     if( FALSE == RegisterClass(&wc) ) 
  256.         return NULL;
  257.  
  258.     RECT rc;
  259.     rc.left     = 0;
  260.     rc.top      = 0;
  261.     rc.right    = SCRAWL_CXBITMAP;
  262.     rc.bottom   = SCRAWL_CYBITMAP;
  263.     AdjustWindowRectEx( &rc, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, 
  264.                         FALSE, WS_EX_APPWINDOW );
  265.  
  266.     HWND hWnd = CreateWindowEx( WS_EX_APPWINDOW, _T("Scrawl"), _T("Scrawl"),  
  267.                                 WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
  268.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  269.                                 rc.right - rc.left, 
  270.                                 rc.bottom - rc.top, 
  271.                                 NULL, NULL, hInst, NULL );
  272.  
  273.     ShowWindow( hWnd, TRUE );
  274.  
  275.     return hWnd;
  276. }
  277.  
  278.  
  279.  
  280.  
  281. //-----------------------------------------------------------------------------
  282. // Name: WndProc()
  283. // Desc: Handles window messages
  284. //-----------------------------------------------------------------------------
  285. LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, 
  286.                           LPARAM lParam )
  287. {
  288.     LRESULT lr = 0;
  289.  
  290.     switch( msg ) 
  291.     {
  292.         // Pass these messages to user defined functions
  293.         HANDLE_MSG( hWnd, WM_CREATE,        OnCreate );
  294.         HANDLE_MSG( hWnd, WM_PAINT,         OnPaint );
  295.         HANDLE_MSG( hWnd, WM_INITMENUPOPUP, OnInitMenuPopup );
  296.         HANDLE_MSG( hWnd, WM_KEYDOWN,       OnKeyDown );
  297.  
  298.         case WM_ACTIVATE:   // sent when window changes active state
  299.             g_bActive = ( WA_ACTIVE == wParam );
  300.  
  301.             // Set exclusive mode access to the mouse based on active state
  302.             SetAcquire();
  303.             return 0;
  304.  
  305.         case WM_NCLBUTTONDOWN:
  306.             switch (wParam)
  307.             {
  308.                 case HTMINBUTTON:
  309.                     ShowWindow( hWnd, SW_MINIMIZE);
  310.                     break;
  311.  
  312.                 case HTCLOSE:
  313.                     PostQuitMessage(0);
  314.                     break;
  315.             }
  316.             return 0;
  317.  
  318.         case WM_ENTERMENULOOP:
  319.             // In case the user opens the menu through the keyboard shortcut
  320.         case WM_ENTERSIZEMOVE:
  321.             // un-acquire device when entering menu or re-sizing
  322.             // this will show the mouse cursor again
  323.             g_bActive = FALSE;
  324.             SetAcquire();
  325.             return 0;
  326.  
  327.         case WM_CAPTURECHANGED:
  328.             // re-acquire device when leaving menu or re-sizing
  329.             // this will hide the default windows mouse cursor
  330.             g_bActive = TRUE;
  331.             SetAcquire();
  332.             return 0;
  333.         
  334.         case WM_SYSCOMMAND:
  335.             lr = 0;
  336.             switch ( LOWORD(wParam) ) 
  337.             {
  338.                 case IDC_CLEAR:
  339.                     OnClear( hWnd );
  340.                     break;
  341.             
  342.                 case IDC_ABOUT:
  343.                     OnAbout( hWnd );
  344.                     break;
  345.             
  346.                 case SC_SCREENSAVE:
  347.                     // eat the screen-saver notification.
  348.                     break;
  349.  
  350.                 case IDC_SENSITIVITY_LOW:
  351.                     g_iSensitivity = -1;
  352.                     break;
  353.  
  354.                 case IDC_SENSITIVITY_NORMAL:
  355.                     g_iSensitivity = 0;
  356.                     break;
  357.  
  358.                 case IDC_SENSITIVITY_HIGH:
  359.                     g_iSensitivity = 1;
  360.                     break;
  361.             
  362.                 default:
  363.                     lr = DefWindowProc( hWnd, msg, wParam, lParam );
  364.                     break;
  365.             }
  366.  
  367.             return lr;
  368.             
  369.  
  370.  
  371.        case WM_DESTROY:
  372.             PostQuitMessage(0);
  373.             break;
  374.     }
  375.  
  376.     return DefWindowProc( hWnd, msg, wParam, lParam );
  377. }
  378.  
  379.  
  380.  
  381.  
  382. //-----------------------------------------------------------------------------
  383. // Name: OnCreate()
  384. // Desc: Handles the WM_CREATE window message
  385. //-----------------------------------------------------------------------------
  386. BOOL OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct )
  387. {
  388.     HRESULT hr;
  389.     HMENU   hMenu;
  390.  
  391.     // Initialize direct input
  392.     hr = InitDirectInput( hWnd );
  393.     if( FAILED(hr) )
  394.     {
  395.         MessageBox( NULL, _T("Error Initializing DirectInput"), 
  396.                     _T("Scrawl"), MB_ICONERROR | MB_OK );
  397.         return FALSE;
  398.     }
  399.  
  400.     // Fix up the popup system menu with custom commands
  401.     hMenu = GetSystemMenu( hWnd, FALSE );
  402.     g_hMenuSense = LoadMenu( (HINSTANCE)GetModuleHandle(NULL), 
  403.                              MAKEINTRESOURCE(IDM_SENSITIVITY) );
  404.  
  405.     EnableMenuItem( hMenu, SC_SIZE,     MF_BYCOMMAND | MF_DISABLED | MF_GRAYED );
  406.     EnableMenuItem( hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED );
  407.     AppendMenu( hMenu, MF_ENABLED | MF_STRING, IDC_CLEAR, _T("C&lear\tDel") );
  408.     AppendMenu( hMenu, MF_ENABLED | MF_STRING, IDC_ABOUT, _T("&About\tF1") );
  409.     AppendMenu( hMenu, MF_ENABLED | MF_STRING | MF_POPUP, 
  410.                 (UINT_PTR)g_hMenuSense, _T("Sensitivit&y") );
  411.  
  412.     return TRUE;    
  413. }
  414.  
  415.  
  416.  
  417.  
  418. //-----------------------------------------------------------------------------
  419. // Name: OnAbout()
  420. // Desc: Handles the IDC_ABOUT window message
  421. //-----------------------------------------------------------------------------
  422. VOID OnAbout( HWND hWnd )
  423. {
  424.     MessageBox( hWnd, TEXT("Scrawl DirectInput Sample v1.0\n\n")
  425.                       TEXT("The Scrawl application demonstrates use of the mouse\n")
  426.                       TEXT("in exclusive mode in a windowed application.\n\n")
  427.                       TEXT("The main mouse button is always the left button,\n")
  428.                       TEXT("and the secondary button is always the right button,\n")
  429.                       TEXT("regardless of any settings the user may have made in\n") 
  430.                       TEXT("Control Panel.\n\n")
  431.                       TEXT("To scrawl, hold down the left button and move the mouse.\n") 
  432.                       TEXT("Click the right mouse button to invoke a pop-up menu. \n")
  433.                       TEXT("From the pop-up menu you can clear the client window,\n")
  434.                       TEXT("set the mouse sensitivity, or close the application."),
  435.                 TEXT("Scrawl"), MB_OK );
  436. }
  437.  
  438.  
  439.  
  440.  
  441. //-----------------------------------------------------------------------------
  442. // Name: OnPaint()
  443. // Desc: Handles the WM_PAINT window message
  444. //-----------------------------------------------------------------------------
  445. VOID OnPaint( HWND hWnd )
  446. {
  447.     PAINTSTRUCT ps;
  448.     HDC         hDC;
  449.     
  450.     hDC = BeginPaint( hWnd, &ps );
  451.     if( NULL == hDC ) 
  452.         return;
  453.  
  454.     BitBlt( hDC, ps.rcPaint.left, ps.rcPaint.top,
  455.             ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
  456.             g_hDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY );
  457.  
  458.     if( g_bActive && g_bShowCursor ) 
  459.         DrawIcon( hDC, g_x - g_dxCrossHot, g_y - g_dyCrossHot, g_hCursorCross );
  460.  
  461.     EndPaint( hWnd, &ps );
  462. }
  463.  
  464.  
  465.  
  466.  
  467. //-----------------------------------------------------------------------------
  468. // Name: OnInitMenuPopup()
  469. // Desc: Handles the WM_INITMENUPOPUP window message
  470. //-----------------------------------------------------------------------------
  471. VOID OnInitMenuPopup( HWND hWnd, HMENU hMenu, UINT item, BOOL fSystemMenu )
  472. {
  473.     for( int iSensitivity = -1; iSensitivity <= 1; iSensitivity++ ) 
  474.     {
  475.         if( g_iSensitivity == iSensitivity ) 
  476.         {
  477.             CheckMenuItem( hMenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  478.                            MF_BYCOMMAND | MF_CHECKED );
  479.         } 
  480.         else 
  481.         {
  482.             CheckMenuItem( hMenu, IDC_SENSITIVITY_NORMAL + iSensitivity,
  483.                            MF_BYCOMMAND | MF_UNCHECKED );
  484.         }
  485.     }
  486. }
  487.  
  488.  
  489.  
  490.  
  491. //-----------------------------------------------------------------------------
  492. // Name: OnKeyDown()
  493. // Desc: Handles the WM_KEYDOWN window message
  494. //-----------------------------------------------------------------------------
  495. VOID OnKeyDown( HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags )
  496. {
  497.     switch( vk ) 
  498.     {
  499.         case '1':
  500.         case '2':
  501.         case '3':
  502.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_SENSITIVITY_NORMAL + vk - '2', 0 );
  503.             break;
  504.  
  505.         case VK_DELETE:
  506.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_CLEAR, 0 );
  507.             break;
  508.  
  509.         case VK_F1:
  510.             PostMessage( hWnd, WM_SYSCOMMAND, IDC_ABOUT, 0 );
  511.             break;
  512.     }
  513. }
  514.  
  515.  
  516.  
  517.  
  518. //-----------------------------------------------------------------------------
  519. // Name: OnClear()
  520. // Desc: Makes the bitmap white
  521. //-----------------------------------------------------------------------------
  522. HRESULT OnClear( HWND hWnd )
  523. {
  524.     PatBlt( g_hDC, 0, 0, SCRAWL_CXBITMAP, SCRAWL_CYBITMAP, WHITENESS );
  525.  
  526.     if( hWnd ) 
  527.         InvalidateRect( hWnd, 0, 0 );
  528.  
  529.     return S_OK;
  530. }
  531.  
  532.  
  533.  
  534.  
  535. //-----------------------------------------------------------------------------
  536. // Name: InvalidateCursorRect()
  537. // Desc: Invalidate the rectangle that contains the cursor.
  538. //       The coordinates are in client coordinates.
  539. //-----------------------------------------------------------------------------
  540. VOID InvalidateCursorRect( HWND hWnd )
  541. {
  542.     RECT rc = { g_x - g_dxCrossHot,             g_y - g_dyCrossHot,
  543.                 g_x - g_dxCrossHot + g_cxCross, g_y - g_dyCrossHot + g_cyCross };
  544.     InvalidateRect( hWnd, &rc, 0 );
  545. }
  546.  
  547.  
  548.  
  549.  
  550. //-----------------------------------------------------------------------------
  551. // Name: UpdateCursorPosition()
  552. // Desc: Move our private cursor in the requested direction, subject
  553. //       to clipping, scaling, and all that other stuff.
  554. //
  555. //       This does not redraw the cursor.  You need to do that yourself.
  556. //-----------------------------------------------------------------------------
  557. VOID UpdateCursorPosition( int dx, int dy )
  558. {   
  559.     // Pick up any leftover fuzz from last time.  This is important
  560.     // when scaling down mouse motions.  Otherwise, the user can
  561.     // drag to the right extremely slow for the length of the table
  562.     // and not get anywhere.
  563.     dx += g_dxFuzz;     
  564.     g_dxFuzz = 0;
  565.  
  566.     dy += g_dyFuzz;     
  567.     g_dyFuzz = 0;
  568.  
  569.     switch( g_iSensitivity ) 
  570.     {
  571.         case 1:     // High sensitivity: Magnify! 
  572.             dx *= 2;
  573.             dy *= 2;
  574.             break;
  575.  
  576.         case -1:    // Low sensitivity: Scale down 
  577.             g_dxFuzz = dx % 2;  // remember the fuzz for next time 
  578.             g_dyFuzz = dy % 2;
  579.             dx /= 2;
  580.             dy /= 2;
  581.             break;
  582.  
  583.         case 0:     // normal sensitivity 
  584.             // No adjustments needed 
  585.             break;
  586.     }
  587.  
  588.     g_x += dx;
  589.     g_y += dy;
  590.  
  591.     // clip the cursor to our client area
  592.     if( g_x < 0 ) 
  593.         g_x = 0;
  594.  
  595.     if( g_x >= SCRAWL_CXBITMAP )
  596.         g_x = SCRAWL_CXBITMAP - 1;
  597.  
  598.     if( g_y < 0 )  
  599.         g_y = 0;
  600.  
  601.     if( g_y >= SCRAWL_CYBITMAP ) 
  602.         g_y = SCRAWL_CYBITMAP - 1;
  603. }
  604.  
  605.  
  606.  
  607.  
  608. //-----------------------------------------------------------------------------
  609. // Name: StartPenDraw()
  610. // Desc: Called when starting pen draw.
  611. //-----------------------------------------------------------------------------
  612. VOID StartPenDraw( HWND hWnd, LEFTBUTTONINFO* plbInfo )
  613. {
  614.     // Hide the cursor while scrawling 
  615.     g_bShowCursor = FALSE;
  616.  
  617.     plbInfo->hdcWindow = GetDC( hWnd );
  618.     MoveToEx( plbInfo->hdcWindow, g_x, g_y, 0 );
  619.     MoveToEx( g_hDC, g_x, g_y, 0 );
  620.  
  621.     SelectObject( plbInfo->hdcWindow, GetStockObject(BLACK_PEN) );
  622.     SelectObject( g_hDC, GetStockObject(BLACK_PEN) );
  623.  
  624.     plbInfo->bMoved = FALSE;
  625.     plbInfo->dwSeqLastSeen = 0;
  626. }
  627.  
  628.  
  629.  
  630.  
  631. //-----------------------------------------------------------------------------
  632. // Name: FinishPenDraw()
  633. // Desc: Called when ending pen draw.
  634. //-----------------------------------------------------------------------------
  635. VOID FinishPenDraw( HANDLE hWnd )
  636. {
  637.     g_bShowCursor = TRUE;
  638. }
  639.  
  640.  
  641.  
  642.  
  643. //-----------------------------------------------------------------------------
  644. // Name: OnLeftButtonDown_FlushMotion()
  645. // Desc: Flush out any motion that we are holding.
  646. //-----------------------------------------------------------------------------
  647. VOID OnLeftButtonDown_FlushMotion( LEFTBUTTONINFO* plbInfo )
  648. {
  649.     if( plbInfo->bMoved ) 
  650.     {
  651.         plbInfo->bMoved = FALSE;
  652.         plbInfo->dwSeqLastSeen = 0;
  653.         LineTo( plbInfo->hdcWindow, g_x, g_y );
  654.         LineTo( g_hDC, g_x, g_y );
  655.     }
  656. }
  657.  
  658.  
  659.  
  660.  
  661. //-----------------------------------------------------------------------------
  662. // Name: OnRightButtonUp()
  663. // Desc: Pop up a context menu.
  664. //-----------------------------------------------------------------------------
  665. VOID OnRightButtonUp( HWND hWnd )
  666. {
  667.     // Place a popup menu where the mouse curent is
  668.     POINT pt;
  669.     pt.x = g_x;
  670.     pt.y = g_y;
  671.     ClientToScreen( hWnd, &pt );
  672.     HMENU hMenuPopup = GetSystemMenu( hWnd, FALSE );
  673.  
  674.     // Hide the cursor while moving it so you don't get annoying flicker.
  675.     ShowCursor( FALSE );
  676.     InvalidateCursorRect( hWnd );
  677.  
  678.     // Unacquire the devices so the user can interact with the menu.
  679.     g_bActive = FALSE;
  680.     SetAcquire();
  681.  
  682.     // Put the Windows cursor at the same location as our virtual cursor.
  683.     SetCursorPos( pt.x, pt.y );
  684.  
  685.     // Show the cursor now that it is moved 
  686.     ShowCursor( TRUE );
  687.     InvalidateCursorRect( hWnd );
  688.  
  689.     // Track the popup menu and return the menu item selected
  690.     UINT iMenuID = TrackPopupMenuEx( hMenuPopup, TPM_RIGHTBUTTON|TPM_RETURNCMD,
  691.                                      pt.x, pt.y, hWnd, 0 );
  692.  
  693.     if( 0 != iMenuID ) // If a menu item was selected
  694.         PostMessage( hWnd, WM_SYSCOMMAND, iMenuID, 0L );
  695. }
  696.  
  697.  
  698.  
  699.  
  700. //-----------------------------------------------------------------------------
  701. // Name: InitDirectInput()
  702. // Desc: Initialize the DirectInput variables.
  703. //-----------------------------------------------------------------------------
  704. HRESULT InitDirectInput( HWND hWnd )
  705. {
  706.     HRESULT hr;
  707.  
  708.     // Register with the DirectInput subsystem and get a pointer
  709.     // to a IDirectInput interface we can use.
  710.     // Create a DInput object
  711.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  712.                                          IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
  713.         return hr;
  714.     
  715.     // Obtain an interface to the system mouse device.
  716.     if( FAILED( hr = g_pDI->CreateDevice( GUID_SysMouse, &g_pMouse, NULL ) ) )
  717.         return hr;
  718.  
  719.     // Set the data format to "mouse format" - a predefined data format 
  720.     //
  721.     // A data format specifies which controls on a device we
  722.     // are interested in, and how they should be reported.
  723.     //
  724.     // This tells DirectInput that we will be passing a
  725.     // DIMOUSESTATE structure to IDirectInputDevice::GetDeviceState.
  726.     if( FAILED( hr = g_pMouse->SetDataFormat( &c_dfDIMouse ) ) )
  727.         return hr;
  728.  
  729.     // Set the cooperativity level to let DirectInput know how
  730.     // this device should interact with the system and with other
  731.     // DirectInput applications.
  732.     if( FAILED( hr = g_pMouse->SetCooperativeLevel( hWnd, 
  733.                                          DISCL_EXCLUSIVE|DISCL_FOREGROUND ) ) )
  734.         return hr;
  735.  
  736.     // Create a win32 event which is signaled when mouse data is availible
  737.     g_hMouseEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  738.     if( NULL == g_hMouseEvent )
  739.         return E_FAIL;
  740.  
  741.     // Give the event to the mouse device
  742.     if( FAILED( hr = g_pMouse->SetEventNotification( g_hMouseEvent ) ) )
  743.         return hr;
  744.  
  745.     // Setup the buffer size for the mouse data
  746.     DIPROPDWORD dipdw;
  747.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  748.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  749.     dipdw.diph.dwObj        = 0;
  750.     dipdw.diph.dwHow        = DIPH_DEVICE;
  751.     dipdw.dwData            = SAMPLE_BUFFER_SIZE; // Arbitary buffer size
  752.  
  753.     if( FAILED( hr = g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
  754.         return hr;
  755.  
  756.     // Not necessary, but nice for left handed users that have
  757.     // their swapped mouse buttons
  758.     g_bSwapMouseButtons = GetSystemMetrics( SM_SWAPBUTTON );
  759.  
  760.     return S_OK;
  761. }
  762.  
  763.  
  764.  
  765.  
  766. //-----------------------------------------------------------------------------
  767. // Name: SetAcquire()
  768. // Desc: Acquire or unacquire the mouse, depending on if the app is active
  769. //       Input device must be acquired before the GetDeviceState is called
  770. //-----------------------------------------------------------------------------
  771. HRESULT SetAcquire()
  772. {
  773.     // Nothing to do if g_pMouse is NULL
  774.     if( NULL == g_pMouse )
  775.         return S_FALSE;
  776.  
  777.     if( g_bActive ) 
  778.         g_pMouse->Acquire();
  779.     else 
  780.         g_pMouse->Unacquire();
  781.  
  782.     return S_OK;
  783. }
  784.  
  785.  
  786.  
  787.  
  788. //-----------------------------------------------------------------------------
  789. // Name: FreeDirectInput()
  790. // Desc: Initialize the DirectInput variables.
  791. //-----------------------------------------------------------------------------
  792. HRESULT FreeDirectInput()
  793. {
  794.     // Unacquire the device one last time just in case 
  795.     // the app tried to exit while the device is still acquired.
  796.     if( g_pMouse ) 
  797.         g_pMouse->Unacquire();
  798.     
  799.     // Release any DirectInput objects.
  800.     SAFE_RELEASE( g_pMouse );
  801.     SAFE_RELEASE( g_pDI );
  802.  
  803.     return S_OK;
  804. }
  805.  
  806.  
  807.  
  808.  
  809. //-----------------------------------------------------------------------------
  810. // Name: OnMouseInput()
  811. // Desc: Handles responding to any mouse input that is generated from
  812. //       the mouse event being triggered.
  813. //-----------------------------------------------------------------------------
  814. VOID OnMouseInput( HWND hWnd )
  815. {
  816.     BOOL                bDone;
  817.     DIDEVICEOBJECTDATA  od;
  818.     DWORD               dwElements;
  819.     HRESULT             hr;
  820.  
  821.     // Invalidate the old cursor so it will be erased 
  822.     InvalidateCursorRect( hWnd );
  823.  
  824.     // Attempt to read one data element.  Continue as long as
  825.     // device data is available.
  826.     bDone = FALSE;
  827.     while( !bDone ) 
  828.     {
  829.         dwElements = 1;
  830.         hr = g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), 
  831.                                       &od, &dwElements, 0 );
  832.  
  833.         if( hr == DIERR_INPUTLOST ) 
  834.         {
  835.             SetAcquire();
  836.             break;
  837.         }
  838.  
  839.         // Unable to read data or no data available
  840.         if( FAILED(hr) || dwElements == 0 ) 
  841.         {
  842.             break;
  843.         }
  844.  
  845.         // Look at the element to see what happened
  846.         switch( od.dwOfs ) 
  847.         {     
  848.             case DIMOFS_X:       // Mouse horizontal motion 
  849.                 UpdateCursorPosition( od.dwData, 0 ); 
  850.                 break;
  851.  
  852.             case DIMOFS_Y:       // Mouse vertical motion 
  853.                 UpdateCursorPosition( 0, od.dwData ); 
  854.                 break;
  855.  
  856.             case DIMOFS_BUTTON0: // Right button pressed or released 
  857.             case DIMOFS_BUTTON1: // Left button pressed or released 
  858.                 // Is the right or a swapped left button down?
  859.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON1 == od.dwOfs ) ||
  860.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON0 == od.dwOfs ) )
  861.                 {
  862.                     if( od.dwData & 0x80 ) 
  863.                     { 
  864.                         // left button pressed, so go into button-down mode 
  865.                         bDone = TRUE;
  866.                         OnLeftButtonDown( hWnd ); 
  867.                     }
  868.                 }
  869.  
  870.  
  871.                 // is the left or a swapped right button down?
  872.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON0 == od.dwOfs ) ||
  873.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON1 == od.dwOfs ) )
  874.                 {
  875.                     if( !(od.dwData & 0x80) ) 
  876.                     {  
  877.                         // button released, so check context menu 
  878.                         bDone = TRUE;
  879.                         OnRightButtonUp( hWnd ); 
  880.                     }
  881.                 }
  882.                 break;
  883.         }
  884.     }
  885.  
  886.     // Invalidate the new cursor so it will be drawn 
  887.     InvalidateCursorRect( hWnd );
  888. }
  889.  
  890.  
  891.  
  892.  
  893. //-----------------------------------------------------------------------------
  894. // Name: OnLeftButtonDown()
  895. // Desc: If we are drawing a curve, then read buffered data and draw
  896. //      lines from point to point.  By reading buffered data, we can
  897. //      track the motion of the mouse accurately without coalescing.
  898. //
  899. //      This function illustrates how a non-message-based program can
  900. //      process buffered data directly from a device, processing
  901. //      messages only occasionally (as required by Windows).
  902. //
  903. //      This function also illustrates how an application can piece
  904. //      together buffered data elements based on the sequence number.
  905. //      A single mouse action (e.g., moving diagonally) is reported
  906. //      as a series of events, all with the same sequence number.
  907. //      Zero is never a valid DirectInput sequence number, so it is
  908. //      safe to use it as a sentinel value.
  909. //-----------------------------------------------------------------------------
  910. VOID OnLeftButtonDown( HWND hWnd )
  911. {
  912.     HRESULT             hr;
  913.     LEFTBUTTONINFO      lbInfo;
  914.     BOOL                bDone;
  915.     DIDEVICEOBJECTDATA  od;
  916.     DWORD               dwElements;
  917.     MSG                 msg;
  918.  
  919.     // For performance, draw directly onto the window's DC instead of
  920.     // invalidating and waiting for the WM_PAINT message.  Of course,
  921.     // we always draw onto our bitmap, too, since that's what really
  922.     // counts.
  923.  
  924.     // hide cursor and initialize button info with cursor position
  925.     StartPenDraw( hWnd, &lbInfo );
  926.     InvalidateCursorRect( hWnd );
  927.     UpdateWindow( hWnd );
  928.  
  929.     // Keep reading data elements until we see a "mouse button up" event.
  930.     bDone = FALSE;
  931.     while( !bDone ) 
  932.     {
  933.         dwElements = 1;
  934.         hr = g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), 
  935.                                       &od, &dwElements, 0 );
  936.         if( FAILED(hr) )       
  937.             break;
  938.  
  939.         // If theres no data available, finish the element 
  940.         // we have been collecting, and then process our message 
  941.         // queue so the system doesn't think the app has hung.
  942.         if( dwElements == 0 ) 
  943.         {
  944.             // if there is a partial motion, flush it out 
  945.             OnLeftButtonDown_FlushMotion( &lbInfo );
  946.  
  947.             while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  948.             {
  949.                 // If it's a quit message, we're outta here 
  950.                 if( msg.message == WM_QUIT ) 
  951.                 {
  952.                     // Re-post the quit message so the
  953.                     // outer loop will see it and exit.
  954.                     PostQuitMessage( (int)msg.wParam );
  955.                     bDone = TRUE;                    
  956.                     break;
  957.                 } 
  958.                 else 
  959.                 {
  960.                     TranslateMessage( &msg );
  961.                     DispatchMessage( &msg );
  962.                 }
  963.             }
  964.             continue;
  965.         }
  966.  
  967.         // If this is the start of a new event, flush out the old one 
  968.         if( od.dwSequence != lbInfo.dwSeqLastSeen ) 
  969.         {
  970.             OnLeftButtonDown_FlushMotion( &lbInfo );
  971.             lbInfo.dwSeqLastSeen = od.dwSequence;
  972.         }
  973.  
  974.         // Look at the element to see what happened 
  975.         switch( od.dwOfs ) 
  976.         {
  977.             case DIMOFS_X:      // Mouse horizontal motion 
  978.                 UpdateCursorPosition( od.dwData, 0 );
  979.                 lbInfo.bMoved = TRUE;
  980.                 break;
  981.  
  982.             case DIMOFS_Y:      // Mouse vertical motion 
  983.                 UpdateCursorPosition( 0, od.dwData );
  984.                 lbInfo.bMoved = TRUE;
  985.                 break;
  986.  
  987.             case DIMOFS_BUTTON0: // Button 0 pressed or released 
  988.             case DIMOFS_BUTTON1: // Button 1 pressed or released 
  989.                 if( ( g_bSwapMouseButtons  && DIMOFS_BUTTON1 == od.dwOfs ) ||
  990.                     ( !g_bSwapMouseButtons && DIMOFS_BUTTON0 == od.dwOfs ) )
  991.                 {
  992.                     if( !(od.dwData & 0x80) ) 
  993.                     { 
  994.                         // Button released, so flush out dregs 
  995.                         bDone = TRUE;
  996.                         OnLeftButtonDown_FlushMotion( &lbInfo ); 
  997.                     }
  998.                 }
  999.                 break;
  1000.         }
  1001.     }
  1002.  
  1003.     ReleaseDC( hWnd, lbInfo.hdcWindow );
  1004.  
  1005.     // Re-show the cursor now that scrawling is finished 
  1006.     FinishPenDraw( hWnd );
  1007.     InvalidateCursorRect( hWnd );
  1008. }
  1009.  
  1010.  
  1011.  
  1012.